---
id: gitlab
title: CI for Databases on GitLab
slug: /guides/ci-platforms/gitlab
---

import InstallationInstructions from '../../components/_installation_instructions.mdx'
import MigratePushCommand from '../../components/migrate-push-command.mdx'
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

![](https://atlasgo.io/uploads/gitlab/gitlab-ci-edited.png)

GitLab is a popular, open-source alternative to GitHub.  In addition to a self-hosted version,
GitLab also offers a hosted version at [gitlab.com](https://gitlab.com). Similar to GitHub, GitLab offers
users storage for Git repositories, issue tracking, and CI/CD pipelines.

In this guide we will demonstrate how to use [GitLab CI](https://docs.gitlab.com/ee/ci/) and Atlas to setup CI
pipelines for your database schema changes.

## Prerequisites

- A GitLab project with an [Atlas migration directory](/versioned/intro).

## Setup

### Install Atlas

<InstallationInstructions />

### Login to Atlas

After installing Atlas locally, you will need to login to your Atlas account.  If you don't have an account yet,
you will be prompted to create one for free during the login process.

You can do this by running the following command:

```bash
atlas login
```

### Push your migrations to Atlas

Once you have logged in, you can push your migrations to Atlas by running the following command:

<MigratePushCommand/>

Atlas will print a URL leading to your migrations on Atlas Cloud.  You can visit this URL to view your migrations.

### Create a Bot Token

To connect GitLab CI to Atlas, you will need to create a bot token.  To learn how to create a bot token, please
visit the [Bot Tokens](/cloud/bots) guide.

## Setup GitLab CI

With your migrations pushed to Atlas, you can now setup GitLab CI to run your migrations on every commit.

### Create a variable for your bot token

In order to connect GitLab CI to Atlas, you will need to [create a variable](https://docs.gitlab.com/ee/ci/variables/#for-a-project)
for your bot token.  To do this:
1. Go to your projects *Settings* page.
2. Click on the *CI/CD* tab.
3. Expand the *Variables* tab.
4. Click "Add Variable".
5. Use `ATLAS_CLOUD_TOKEN` as the variable key and paste the bot token you created earlier as the variable value.
6. Set the "Mask Variable" checkbox to true to prevent the token from being printed in the CI logs.
7. Unset the "Protect Variable" checkbox so that this job can be run on all branches (necessary for running on merge requests).

### Create a `.gitlab-ci.yml` file

Next, you will need to create a `.gitlab-ci.yml` file in the root of your repository.  This file will tell GitLab CI
how to run your Atlas CI pipeline.

<Tabs>
<TabItem value="sqlite" label="SQLite">

```yaml
image: ubuntu:latest

stages:
  - lint
  - push

before_script:
  - apt-get update -q && apt-get install -y curl
  - 'which atlas || (curl -sSf https://atlasgo.sh | sh)'
  - atlas login --token $ATLAS_CLOUD_TOKEN

lint migrations:
  stage: lint
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
      when: always
    - if: '$CI_MERGE_REQUEST_ID'
      changes:
        - migrations/*
      when: always
      allow_failure: false
  script:
    - |
      if [ -n "$CI_MERGE_REQUEST_IID" ]; then
        URL="${CI_PROJECT_URL}/-/merge_requests/${CI_MERGE_REQUEST_IID}"
      else
        URL="${CI_PROJECT_URL}/-/commit/${CI_COMMIT_SHA}"
      fi

      ATLAS_CONTEXT=$(cat <<EOF
      {
        "repo": "$CI_PROJECT_PATH",
        "branch": "$CI_COMMIT_REF_NAME",
        "commit": "$CI_COMMIT_SHA",
        "url": "$URL"
      }
      EOF
      )
    - atlas migrate lint --base atlas://gitlab -w --dev-url "sqlite://demo?mode=memory" --context "$ATLAS_CONTEXT"

push migrations:
  stage: push
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
  script:
    - |
      ATLAS_CONTEXT=$(cat <<EOF
      {
        "repo": "$CI_PROJECT_PATH",
        "branch": "$CI_COMMIT_REF_NAME",
        "commit": "$CI_COMMIT_SHA",
        "url": "${CI_PROJECT_URL}"
      }
      EOF
      )
    - atlas migrate push gitlab --dev-url "sqlite://demo?mode=memory" --context "$ATLAS_CONTEXT"
```

</TabItem>
<TabItem value="postgres" label="PostgreSQL">

```yaml
image: ubuntu:latest

services:
  - postgres

variables:
  POSTGRES_DB: db
  POSTGRES_USER: user
  POSTGRES_PASSWORD: pass

stages:
  - lint
  - push

before_script:
  - apt-get update -q && apt-get install -y curl
  - 'which atlas || (curl -sSf https://atlasgo.sh | sh)'
  - atlas login --token $ATLAS_CLOUD_TOKEN

lint migrations:
  stage: lint
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
      when: always
    - if: '$CI_MERGE_REQUEST_ID'
      changes:
        - migrations/*
      when: always
      allow_failure: false
  script:
    - |
      if [ -n "$CI_MERGE_REQUEST_IID" ]; then
        URL="${CI_PROJECT_URL}/-/merge_requests/${CI_MERGE_REQUEST_IID}"
      else
        URL="${CI_PROJECT_URL}/-/commit/${CI_COMMIT_SHA}"
      fi

      ATLAS_CONTEXT=$(cat <<EOF
      {
        "repo": "$CI_PROJECT_PATH",
        "branch": "$CI_COMMIT_REF_NAME",
        "commit": "$CI_COMMIT_SHA",
        "url": "$URL"
      }
      EOF
      )
    - atlas migrate lint --base atlas://gitlab -w --dev-url "postgres://user:pass@postgres/db?sslmode=disable" --context "$ATLAS_CONTEXT"

push migrations:
  stage: push
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
  script:
    - |
      ATLAS_CONTEXT=$(cat <<EOF
      {
        "repo": "$CI_PROJECT_PATH",
        "branch": "$CI_COMMIT_REF_NAME",
        "commit": "$CI_COMMIT_SHA",
        "url": "${CI_PROJECT_URL}"
      }
      EOF
      )
    - atlas migrate push gitlab --dev-url "postgres://user:pass@postgres/db?sslmode=disable" --context "$ATLAS_CONTEXT"
```

</TabItem>
<TabItem value="mysql" label="MySQL">

```yaml
image: ubuntu:latest

services:
  - mysql

variables:
  MYSQL_DATABASE: db
  MYSQL_ROOT_PASSWORD: pass

stages:
  - lint
  - push

before_script:
  - apt-get update -q && apt-get install -y curl
  - 'which atlas || (curl -sSf https://atlasgo.sh | sh)'
  - atlas login --token $ATLAS_CLOUD_TOKEN

lint migrations:
  stage: lint
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
      when: always
    - if: '$CI_MERGE_REQUEST_ID'
      changes:
        - migrations/*
      when: always
      allow_failure: false
  script:
    - |
      if [ -n "$CI_MERGE_REQUEST_IID" ]; then
        URL="${CI_PROJECT_URL}/-/merge_requests/${CI_MERGE_REQUEST_IID}"
      else
        URL="${CI_PROJECT_URL}/-/commit/${CI_COMMIT_SHA}"
      fi

      ATLAS_CONTEXT=$(cat <<EOF
      {
        "repo": "$CI_PROJECT_PATH",
        "branch": "$CI_COMMIT_REF_NAME",
        "commit": "$CI_COMMIT_SHA",
        "url": "$URL"
      }
      EOF
      )
    - atlas migrate lint --base atlas://gitlab -w --dev-url "mysql://root:pass@mysql/db" --context "$ATLAS_CONTEXT"

push migrations:
  stage: push
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
  script:
    - |
      ATLAS_CONTEXT=$(cat <<EOF
      {
        "repo": "$CI_PROJECT_PATH",
        "branch": "$CI_COMMIT_REF_NAME",
        "commit": "$CI_COMMIT_SHA",
        "url": "${CI_PROJECT_URL}"
      }
      EOF
      )
    - atlas migrate push gitlab --dev-url "mysql://root:pass@mysql/db" --context "$ATLAS_CONTEXT"
```

</TabItem>
</Tabs>

Let's break down what this file is doing:
1. The `image` directive tells GitLab CI to use the `ubuntu:latest` Docker image to run your CI jobs.
2. The `stages` directive defines the stages that your CI pipeline will run.  In this case, we have two stages:
   `lint` and `push`. In the `lint` stage, we will run the `atlas migrate lint` command to verify that our migrations
   are valid and safe. In the `push` stage, we will run the `atlas migrate push` command to push our migrations to Atlas.
3. The `before_script` directive defines a set of commands that will be run before each job.  In this case, we are
   installing the `atlas` CLI and logging into Atlas using the bot token we created earlier.
4. The `lint migrations` job will run the `atlas migrate lint` command.  This job will run on all commits to your default
   branch and on all merge requests.  This job will fail if the migrations are invalid.
5. The `push migrations` job will run the `atlas migrate push` command.  This job will only run on commits to your default
    branch.

### Commit and push your changes

Now that you have created your `.gitlab-ci.yml` file, you can commit and push your changes to GitLab.

From this point on, whenever you make a change to your migrations and push them to GitLab, GitLab CI will run your
Atlas CI pipeline to verify their safety. To view a detailed analysis of your schema changes, look at the logs for
a direct link to your Atlas Cloud account:
```text
$ which atlas || (curl -sSf https://atlasgo.sh | sh)
Downloading https://release.gitee.com/damengde/atlas/atlas-linux-amd64-latest
Installation successful!

$ atlas login --token $ATLAS_CLOUD_TOKEN
You are now connected to "rotemtam85" on Atlas Cloud.

$ atlas migrate lint --base atlas://gitlab -w --dev-url "sqlite://demo?mode=memory" --context "$ATLAS_CONTEXT"
// highlight-next-line
https://rotemtam85.atlasgo.cloud/ci-runs/8589934616
Cleaning up project directory and file based variables
```

Once you merge your changes to your default branch, the pipeline will
push your latest changes to Atlas, so they can be deployed. Similarly, to get a visual
representation of your database schema, follow the link from the logs to Atlas Cloud:

```text
$ which atlas || (curl -sSf https://atlasgo.sh | sh)
Downloading https://release.gitee.com/damengde/atlas/atlas-linux-amd64-latest
Installation successful!

$ atlas login --token $ATLAS_CLOUD_TOKEN
You are now connected to "rotemtam85" on Atlas Cloud.

$ atlas migrate push gitlab --dev-url "sqlite://demo?mode=memory" --context "$ATLAS_CONTEXT"
// highlight-next-line
https://rotemtam85.atlasgo.cloud/dirs/4294967302
```

## Next Steps: Deployments

Congratulations! You have successfully setup GitLab CI to lint and push your migration
directory to Atlas Cloud. The next step is to deploy your migrations to your production database
directly from Atlas Cloud using the [CLI](/guides/deploying/from-cloud).
